# -*- coding: utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class CDTEditorContext(QWidget):
	def __init__(self, parent):
		QWidget.__init__(self, parent)
		self.cursor = QPoint(0, 0) # coord
		self.padding = [5, 5]
		
		self._lines = ['']
		self._attrs = ['']
		self._currentLine = 0
		self._maxLine = 0
				
		self.setFocusPolicy(Qt.ClickFocus)
		# enable the input method
		self.setAttribute(Qt.WA_InputMethodEnabled, True);

	def keyPressEvent(self, event):
		""" handle the key event """
		k = event.key()
		cx, cy = self.__getCursor()
		
		if k in [Qt.Key_Enter, Qt.Key_Return]:
			line, attr = self.__getCurrentLineAttr()
			
			# increase the line
			self._currentLine += 1
			self._maxLine += 1
			
			# alias
			cur = self._currentLine
			
			# truncate the string behind the cursor to the next line
			self._lines = self.__insertToListAt(self._lines, line[cx:], cur)
			self._attrs = self.__insertToListAt(self._attrs, attr[cx:], cur)
			self._lines[cur-1] = line[0:cx]
			self._attrs[cur-1] = attr[0:cx]
						
			# update cursor
			cx = 0
			cy += 1
			
		elif k in [Qt.Key_Left, Qt.Key_Up, Qt.Key_Right, Qt.Key_Down]: # arrow
			move_tbl = [[-1, 0], [0, -1], [1, 0], [0, 1]]
			cx += move_tbl[k - Qt.Key_Left][0]
			cy += move_tbl[k - Qt.Key_Left][1]

			# alias
			buf = self.__getCurrentLine()

			# check cursor x
			if cx < 0:
				cx = 0
			elif cx > len(buf):
				cx = len(buf)
				
			# check cursor y
			if cy < 0:
				cy = 0
			elif cy > self._maxLine:
				cy = self._maxLine
			else:
				self._currentLine = cy
				buf = self.__getCurrentLine()
				if len(buf) < cx:
					cx = len(buf)
						
		elif k == Qt.Key_Backspace:
			if cx == 0: # line head
				if cy > 0:
					origLine, origAttr = self.__getCurrentLineAttr()
					orig = self._currentLine
					self._currentLine -= 1
					self._maxLine -= 1
					
					newLine, newAttr = self.__getCurrentLineAttr()
					
					cx = len(newLine)
					cy -= 1
					newLine += origLine
					newAttr += origAttr
					self.__setCurrentLineAttr(newLine, newAttr)
					
					self._lines = self.__removeFromListAt(self._lines, orig)
					self._attrs = self.__removeFromListAt(self._attrs, orig)
			else:
				cx -= 1
				line,attr = self.__getCurrentLineAttr()
				line = self.__removeFromListAt(line, cx)
				attr = self.__removeFromListAt(attr, cx)
				self.__setCurrentLineAttr(line, attr)
				
		elif k in [Qt.Key_Home, Qt.Key_End]:
			target = [0, len(self.__getCurrentLine())]
			cx = target[k-Qt.Key_Home]
			
		elif k in [Qt.Key_PageUp, Qt.Key_PageDown]:
			# TODO: scroll
			pass
		else: # chars
			line, attr = self.__getCurrentLineAttr()
			newLine = line[0:cx] + event.text() + line[cx:]
			newAttr = attr[0:cx] + '0'*len(event.text()) + attr[cx:]
			self.__setCurrentLineAttr(newLine, newAttr)
			cx += len(event.text())
		
		self.__setCursor(cx, cy)
		self.update()
		event.accept()

	def inputMethodEvent(self, event):
		""" handle the input method event """
		cmtStr = event.commitString()
		preStr = event.preeditString()
		
		if len(cmtStr) > 0:
			for i in range(len(cmtStr)):
				ev = QKeyEvent(QEvent.KeyPress, 0, Qt.NoModifier, cmtStr[i])
				self.keyPressEvent(ev)
		
		if len(preStr) == 0:
			event.accept()
			return
			
		event.accept()

	def inputMethodQuery(self, query):
		""" handle inputMethodQuery"""
		if query == Qt.ImMicroFocus:
			return QVariant(QRect(0, 0, 0, 0))
		else:
			return QWidget.inputMethodQuery(self, query)

	def paintEvent(self, event):
		self.setFont(QFont('Monaco', 16.0))
		
		widgetRect = event.rect()
		# fill this area
		painter = QPainter(self)
		b = QColor.fromRgb(0, 0, 0)
		painter.fillRect(widgetRect, b)
		
		pen = QPen()
		fm = self.fontMetrics()
		
		# draw string
		pen.setColor(QColor.fromRgb(255, 255, 255)) # set forecolor - white by default
		painter.setPen(pen)
		for i in range(len(self._lines)):
			line = self._lines[i]
			for j in range(len(line)):
				# TODO: attr
				painter.drawText(self.padding[0] + fm.width(line[0:j]),
								(i+1)*fm.height() + self.padding[1], line[j])
		
		# cursor
		b = QColor.fromRgb(0, 255, 0) # set a green brush
		line = self._lines[self.cursor.y()]
		cursorRect = QRect(self.padding[0] + fm.width(line[0:self.cursor.x()]),
							self.padding[1] + self.cursor.y() * fm.height() + fm.descent(), 2, fm.height())
		painter.fillRect(cursorRect, b)

	def setEditor(self, e):
		self.editor = e

	def newPaintEvent(self, event):
		pt = QPainter(self)
		bufg = CDTBufferedGraphics(pt)
		self.editor.paint(bufg, event.rect)
		
	def __setCursor(self, x, y):
		self.cursor.setX(x)
		self.cursor.setY(y)
	
	def __getCursor(self):
		return self.cursor.x(), self.cursor.y()
	
	def __getCurrentLineAttr(self):
		return self._lines[self._currentLine], self._attrs[self._currentLine]
	
	def __setCurrentLineAttr(self, line, attr):
		self._lines[self._currentLine] = line
		self._attrs[self._currentLine] = attr
	
	def __getCurrentLine(self):
		return self._lines[self._currentLine]

	def __setCurrentLine(self, buf):
		self._lines[self._currentLine] = buf

	def __getCurrentAttr(self):
		"""docstring for __getCurrentAttr"""
		return self._attrs[self._currentLine]

	def __setCurrentAttr(self, buf):
		self._attrs[self._currentLine] = buf
		
	def __insertToListAt(self, orig, token, at):
		return orig[0:at] + [token] + orig[at:]
		
	def __removeFromListAt(self, orig, at):
		return orig[0:at] + orig[at+1:]